-
-
Notifications
You must be signed in to change notification settings - Fork 638
Add async_react_component and cached_async_react_component helpers #2139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…2138) Add concurrent React component rendering support for React on Rails Pro. Multiple async_react_component calls now execute their HTTP rendering requests in parallel instead of sequentially. New features: - async_react_component: Returns AsyncValue immediately, renders concurrently - cached_async_react_component: Async rendering with fragment caching support - AsyncRendering concern: Controller mixin with enable_async_react_rendering - Supports all cached_react_component options (:cache_key, :cache_options, :if, :unless) Usage: ```ruby class ProductsController < ApplicationController include ReactOnRailsPro::AsyncRendering enable_async_react_rendering only: [:show] end ``` ```erb <% header = async_react_component("Header", props: @header_props) %> <% sidebar = async_react_component("Sidebar", props: @sidebar_props) %> <%= header.value %> <%= sidebar.value %> ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
WalkthroughAdds barrier-backed asynchronous server rendering: new helpers for async and cached async component rendering, AsyncValue/ImmediateAsyncValue wrappers, an AsyncRendering controller concern to establish an Async::Barrier context, and supporting tests, demo components, and routes. Changes
Sequence Diagram(s)sequenceDiagram
participant Controller
participant Helper
participant Cache as Rails.cache
participant Barrier as Async::Barrier
participant Task as Background Task
participant Component as Node/Server Component
rect rgb(200,240,220)
Note over Controller,Cache: Cache hit (sync)
Controller->>Helper: cached_async_react_component(name, opts)
Helper->>Cache: read(cache_key)
Cache-->>Helper: cached_html
Helper-->>Controller: ImmediateAsyncValue(cached_html)
end
rect rgb(220,220,255)
Note over Controller,Barrier: Cache miss / uncached async flow
Controller->>Helper: async_react_component(...) / cached_async_react_component(...)
Helper->>Cache: read(cache_key)
Cache-->>Helper: miss
Helper->>Barrier: barrier.async { schedule render task }
Barrier->>Task: spawn render task
Helper-->>Controller: AsyncValue(task)
Task->>Component: invoke server render (async)
Component-->>Task: rendered_html or error
Task->>Cache: write(cache_key, rendered_html) -- optional on cache path
Task-->>AsyncValue: complete with rendered_html
Controller->>AsyncValue: value() (blocks until ready)
AsyncValue-->>Controller: rendered_html (html_safe)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review - PR #2139: Async React Component RenderingGreat work on implementing concurrent React component rendering! This is a valuable performance enhancement. Here's my detailed review: ✅ Strengths
🔍 Code Quality Observations1. AsyncValue Error Handling ✅ GoodThe def value
@task.wait
endThis is correct - exceptions naturally bubble up from the async task. 2. Barrier Cleanup in AsyncRendering Concern ✅ Gooddef wrap_in_async_react_context
Sync do
@react_on_rails_async_barrier = Async::Barrier.new
yield
ensure
@react_on_rails_async_barrier = nil
end
endProper cleanup ensures barrier is reset after request. 3. Cache Key Construction - Consider EnhancementIn cache_key = ReactOnRailsPro::Cache.react_component_cache_key(component_name, raw_options)Question: Does this cache key include the async/non-async distinction? If a user calls both Recommendation: This is likely correct (they should share cache), but worth verifying that the cache semantics match the existing
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
react_on_rails_pro/lib/react_on_rails_pro/async_value.rb (1)
12-37: LGTM! Clean async wrapper implementation.The
AsyncValueclass provides a clean interface for wrapping async tasks. The methods correctly delegate to the underlyingAsync::Task.One observation:
AsyncValueexposescomponent_nameviaattr_reader, butImmediateAsyncValue(from the relevant snippets atreact_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rblines 7-25) does not have this attribute. If these classes are meant to be used polymorphically, consider addingcomponent_nametoImmediateAsyncValuefor interface consistency, or document that it's optional.react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rb (1)
8-26: Consider addingcomponent_namefor interface parity withAsyncValue.
AsyncValueexposescomponent_nameas a public attribute, butImmediateAsyncValuedoes not. If code needs to introspect the component name from either type of value, this asymmetry could cause issues.class ImmediateAsyncValue - def initialize(value) + def initialize(value, component_name: nil) @value = value + @component_name = component_name end - attr_reader :value + attr_reader :value, :component_namereact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb (1)
851-878: Concurrent execution test may be flaky.The test relies on
Async::Task.current.yieldto force fiber interleaving and verify concurrent execution. While this approach works, the assertionexpect(max_concurrent).to eq(2)could be sensitive to scheduling. Consider adding a comment explaining the test's intent, or using a more deterministic synchronization mechanism.Consider adding a brief comment:
it "executes multiple components concurrently" do call_count = 0 max_concurrent = 0 mutex = Mutex.new + # Mock react_component to track concurrent execution. + # Uses yield to allow fiber interleaving, verifying both tasks run simultaneously. allow(self).to receive(:react_component) do |_name, _opts|react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb (1)
41-51: Consider explicitly stopping the barrier to handle incomplete tasks.If a view finishes without calling
.valueon allAsyncValueobjects (e.g., due to early return or conditional rendering), tasks may still be running. Callingbarrier.stopinensurewould gracefully cancel any incomplete tasks and avoid potential resource leaks.def wrap_in_async_react_context require "async" require "async/barrier" Sync do @react_on_rails_async_barrier = Async::Barrier.new yield ensure + @react_on_rails_async_barrier&.stop @react_on_rails_async_barrier = nil end endreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb (2)
376-386: Cache read options may not apply as expected.
Rails.cache.readaccepts a second argument for options, but typically read options (likerace_condition_ttl) are write-only. Ifcache_optionscontains only write-time options, passing them toreadis harmless but unnecessary. Consider removing the second argument for clarity:- cached_result = Rails.cache.read(cache_key, cache_options) + cached_result = Rails.cache.read(cache_key)
416-422: Consider adding&blockto method signature for consistency.The method uses
yieldimplicitly. While this works, other methods in this file (e.g.,render_async_react_component_uncached) explicitly declare&block. Adding it here improves readability and consistency.- def prepare_async_render_options(raw_options) + def prepare_async_render_options(raw_options, &block) raw_options.merge( - props: yield, + props: block.call, skip_prerender_cache: true, auto_load_bundle: ReactOnRails.configuration.auto_load_bundle || raw_options[:auto_load_bundle] ) end
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb(2 hunks)react_on_rails_pro/lib/react_on_rails_pro.rb(1 hunks)react_on_rails_pro/lib/react_on_rails_pro/async_value.rb(1 hunks)react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb(1 hunks)react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rb(1 hunks)react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb(1 hunks)react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb(1 hunks)react_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rb(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{rb,rake}
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS run
bundle exec rubocopand fix ALL violations before every commit/push
Files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS ensure files end with a newline character before committing/pushing
Files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
🧠 Learnings (19)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to lib/react_on_rails/*.rb : Create RBS signature files for new Ruby files in `lib/react_on_rails/` by adding corresponding `.rbs` file in `sig/react_on_rails/`, adding to Steepfile, and validating before committing
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to sig/react_on_rails/**/*.rbs : In RBS type signature files, define type signatures that correspond to Ruby files in `lib/react_on_rails/`
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rb
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rbreact_on_rails_pro/lib/react_on_rails_pro/async_value.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to spec/dummy/e2e/playwright/**/*.spec.js : Use Rails helpers (`appFactories`, `appEval`) in Playwright tests instead of UI-based setup to leverage factory_bot and arbitrary Ruby code execution
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: The `react_on_rails_pro/` directory has its own Prettier/ESLint configuration and must be linted separately
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2024-10-08T20:53:47.076Z
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes (features, bug fixes, breaking changes, deprecations, performance improvements) to the open-source React on Rails gem and npm package
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-13T14:29:49.267Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: spec/react_on_rails/utils_spec.rb:218-218
Timestamp: 2025-02-13T14:29:49.267Z
Learning: In RSpec tests, prefer using local variables over constants within test blocks to avoid constant redefinition warnings and maintain better test isolation.
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
🧬 Code graph analysis (4)
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rb (1)
react_on_rails_pro/lib/react_on_rails_pro/async_value.rb (6)
initialize(15-18)value(22-24)attr_reader(12-37)resolved?(26-28)to_s(30-32)html_safe(34-36)
react_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rb (2)
react_on_rails_pro/lib/react_on_rails_pro/async_value.rb (4)
value(22-24)resolved?(26-28)to_s(30-32)html_safe(34-36)react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rb (3)
resolved?(15-17)to_s(19-21)html_safe(23-25)
react_on_rails_pro/lib/react_on_rails_pro.rb (1)
react_on_rails_pro/spec/dummy/client/node-renderer.js (1)
require(8-8)
react_on_rails_pro/lib/react_on_rails_pro/async_value.rb (1)
react_on_rails_pro/lib/react_on_rails_pro/immediate_async_value.rb (5)
initialize(8-26)initialize(9-11)resolved?(15-17)to_s(19-21)html_safe(23-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: examples (3.4, latest)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: pro-lint-js-and-ruby
- GitHub Check: claude-review
🔇 Additional comments (13)
react_on_rails_pro/lib/react_on_rails_pro.rb (1)
23-25: LGTM! Correct integration of async rendering components.The new requires are appropriately placed and follow the existing pattern of loading concerns before their related classes.
react_on_rails_pro/spec/react_on_rails_pro/immediate_async_value_spec.rb (1)
1-46: LGTM! Comprehensive test coverage for ImmediateAsyncValue.Tests cover all public methods with clear, focused examples. The test structure follows good RSpec conventions.
react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb (1)
1-101: LGTM! Thorough async behavior testing.The tests effectively validate AsyncValue behavior including:
- Proper async task coordination using
Syncblocks- Exception propagation verification
- Clever use of
Async::Barrierto test the unresolved statereact_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb (4)
820-827: LGTM! Good error handling test.Properly verifies that
async_react_componentraises an informative error when called outside the async context established byAsyncRenderingconcern.
890-897: LGTM! Consistent error handling for cached variant.Matches the error behavior of
async_react_componentfor calls outside async context.
982-992: LGTM! Important validation tests.Good coverage for argument validation:
- Props must be passed as a block (not directly)
cache_keyis requiredThese tests ensure users get clear error messages for common mistakes.
951-980: LGTM! Conditional caching tests are thorough.Tests verify both
:ifand:unlessoptions correctly disable caching when conditions are met, usingRandomValuecomponent to detect re-rendering.react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb (1)
1-27: Good documentation with clear usage examples.The examples cover common use cases (all actions, specific actions, excluding actions) which will help users adopt this feature.
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb (5)
237-249: LGTM!Clear error message, proper barrier usage, and correct AsyncValue construction.
270-275: LGTM!Follows the same pattern as
cached_react_componentwith proper validation viacheck_caching_options!.
393-401: LGTM!Correctly creates async task without caching when conditional caching is disabled.
403-414: LGTM!The cache write-through on completion is correctly implemented inside the async task. The benign race condition on concurrent cache misses follows standard cache-aside semantics.
220-422: Run RuboCop to validate the new code.As per coding guidelines, run
bundle exec rubocopand fix any violations before committing these changes.
b7f640b to
8aaf212
Compare
Code Review: Async React Component RenderingI've completed a comprehensive review of PR #2139. This is a well-implemented feature with solid test coverage. Below are my findings organized by category: ✅ StrengthsCode Quality
Test Coverage
Architecture
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx (1)
1-21: Clarify'use client'on server file and consider de‑duplicating markupThis
.server.jsxfile exports a server render function, so keeping a'use client';directive at the top may be confusing unless your bundler explicitly relies on it here—if it’s not required, consider dropping it. Also, theDelayedComponentJSX is identical to the client version; if you expect this to evolve, you might optionally share the small presentational component between server/client variants to keep them in sync.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb(2 hunks)react_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb(1 hunks)react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsx(1 hunks)react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx(1 hunks)react_on_rails_pro/spec/dummy/config/routes.rb(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS ensure files end with a newline character before committing/pushing
Files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsxreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
**/*.{rb,rake}
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS run
bundle exec rubocopand fix ALL violations before every commit/push
Files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rb
🧠 Learnings (17)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsxreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes (features, bug fixes, breaking changes, deprecations, performance improvements) to the open-source React on Rails gem and npm package
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsxreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2024-07-27T10:08:35.868Z
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsxreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsxreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-02-13T19:09:15.991Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsxreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to sig/react_on_rails/**/*.rbs : In RBS type signature files, define type signatures that correspond to Ruby files in `lib/react_on_rails/`
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: The `react_on_rails_pro/` directory has its own Prettier/ESLint configuration and must be linted separately
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/config/routes.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsxreact_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to spec/dummy/e2e/playwright/**/*.spec.js : Use Rails helpers (`appFactories`, `appEval`) in Playwright tests instead of UI-based setup to leverage factory_bot and arbitrary Ruby code execution
Applied to files:
react_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to lib/react_on_rails/*.rb : Create RBS signature files for new Ruby files in `lib/react_on_rails/` by adding corresponding `.rbs` file in `sig/react_on_rails/`, adding to Steepfile, and validating before committing
Applied to files:
react_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
🧬 Code graph analysis (2)
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb (1)
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb (1)
enable_async_react_rendering(34-36)
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx (1)
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsx (1)
DelayedComponent(6-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: examples (3.4, latest)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: pro-lint-js-and-ruby
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: claude-review
🔇 Additional comments (4)
react_on_rails_pro/spec/dummy/config/routes.rb (1)
41-41: Route wiring for async demo is consistentPath, controller action, and named route follow the existing Pro routes convention and line up with the new action/view; no issues spotted.
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsx (1)
1-12: Client DelayedComponent demo looks goodProps/defaults match the server variant and the markup is minimal and clear; this is a fine client-side counterpart for the async demo.
react_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb (1)
1-34: Async demo view correctly exercises async_react_componentThe view starts all 10 async renders non-blockingly, then resolves them via
component.valueand reports elapsed time, which neatly demonstrates the intended concurrency behavior.react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb (1)
6-8: AsyncRendering wiring and demo action are well-scopedIncluding
ReactOnRailsPro::AsyncRenderingand limitingenable_async_react_renderingto:async_components_demokeeps the async context opt-in, and the new action cleanly renders the matching demo template used by the route.Also applies to: 163-167
Code Review for PR #2139: Add async_react_component and cached_async_react_component helpersThis is an excellent PR that adds a valuable performance optimization feature to React on Rails Pro! The implementation is well-thought-out and comprehensive. Here's my detailed feedback: ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Test Coverage 🎯
3. Performance Considerations
4. Code Quality
🔍 Areas for Improvement1. Missing CHANGELOG Update
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb(1 hunks)react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb(2 hunks)react_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb(1 hunks)react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsx(1 hunks)react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx(1 hunks)react_on_rails_pro/spec/dummy/config/routes.rb(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- react_on_rails_pro/spec/dummy/config/routes.rb
- react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsx
- react_on_rails_pro/spec/dummy/app/views/pages/pro/async_components_demo.html.erb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{rb,rake}
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS run
bundle exec rubocopand fix ALL violations before every commit/push
Files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS ensure files end with a newline character before committing/pushing
Files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
🧠 Learnings (18)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes (features, bug fixes, breaking changes, deprecations, performance improvements) to the open-source React on Rails gem and npm package
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to sig/react_on_rails/**/*.rbs : In RBS type signature files, define type signatures that correspond to Ruby files in `lib/react_on_rails/`
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: The `react_on_rails_pro/` directory has its own Prettier/ESLint configuration and must be linted separately
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rbreact_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2024-10-08T20:53:47.076Z
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to lib/react_on_rails/*.rb : Create RBS signature files for new Ruby files in `lib/react_on_rails/` by adding corresponding `.rbs` file in `sig/react_on_rails/`, adding to Steepfile, and validating before committing
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rbreact_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.
Applied to files:
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
📚 Learning: 2025-02-13T19:09:15.991Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/RSCWebpackLoader.ts:0-0
Timestamp: 2025-02-13T19:09:15.991Z
Learning: In React Server Components webpack loader, using `new Function('return import("react-server-dom-webpack/node-loader")')()` is necessary as a workaround to bypass TypeScript compilation issues with direct dynamic imports.
Applied to files:
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
🧬 Code graph analysis (2)
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb (1)
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb (1)
enable_async_react_rendering(34-36)
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx (1)
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.client.jsx (1)
DelayedComponent(6-10)
🪛 GitHub Actions: React on Rails Pro - Lint
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
[error] 1-1: ESLint: 'no-promise-executor-return' violation. Return values from promise executor functions cannot be read. (no-promise-executor-return)
🪛 GitHub Check: pro-lint-js-and-ruby
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
[failure] 18-18:
Return values from promise executor functions cannot be read
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: package-js-tests
- GitHub Check: dummy-app-node-renderer-e2e-tests
- GitHub Check: rspec-dummy-app-node-renderer
- GitHub Check: examples (3.4, latest)
- GitHub Check: examples (3.2, minimum)
🔇 Additional comments (5)
react_on_rails_pro/lib/react_on_rails_pro/concerns/async_rendering.rb (4)
1-27: LGTM! Clear documentation and proper concern structure.The module documentation effectively explains the purpose and usage patterns with helpful examples. The concern follows Rails conventions properly.
29-37: LGTM! Proper filter registration.The class method correctly delegates to
around_actionwith filter options, following Rails conventions.
55-66: LGTM! Helpful error message for unresolved tasks.The check correctly identifies pending tasks and provides clear guidance on how to fix the issue. The error message will help developers debug missing
.valuecalls.
41-53: Remove invalid runtime dependency concern; clarify error handling behavior.The implementation properly handles cleanup when
yieldraises an exception:
Error handling is sound: The
ensureblock (lines 49-52) will execute regardless of exceptions, callingbarrier.stopto terminate all pending async tasks. The concern about tasks being "stopped but not logged" is valid—check_for_unresolved_async_components(which logs pending tasks) is skipped on error. However, this appears intentional to avoid log noise on failed actions.Runtime dependency is satisfied: The async gem is already declared in
react_on_rails_pro.gemspecass.add_runtime_dependency "async", ">= 2.6", so therequirestatements will not fail due to a missing gem.If developers should be warned about unresolved tasks even when actions fail, consider moving the unresolved component check into the
ensureblock to guarantee it always executes.react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb (1)
6-8: LGTM! Proper async rendering setup for demo.The controller correctly:
- Includes the
AsyncRenderingconcern (Line 6)- Enables async rendering only for the demo action (Line 8)
- Provides a well-documented demo action (Lines 163-167)
The implementation follows the exact pattern documented in the concern's examples.
Also applies to: 163-167
| 'use client'; | ||
|
|
||
| import React from 'react'; | ||
|
|
||
| // Component that simulates a slow render by delaying for 1 second | ||
| // Used to demonstrate concurrent rendering with async_react_component | ||
| const DelayedComponent = ({ index, delayMs = 1000 }) => ( | ||
| <div style={{ padding: '10px', margin: '5px', border: '1px solid #ccc' }}> | ||
| <strong>Component {index}</strong> - Rendered after {delayMs}ms delay | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove 'use client' directive from server component file.
The file is named DelayedComponent.server.jsx, which indicates a server component, but it contains a 'use client' directive (Line 1). These are contradictory:
- Server components (
.server.jsx) run only on the server - The
'use client'directive marks a component boundary for client-side rendering
Based on the async function export pattern (Lines 14-21) and the server-side delay simulation, this should be a server component.
Apply this diff to remove the directive:
-'use client';
-
import React from 'react';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'use client'; | |
| import React from 'react'; | |
| // Component that simulates a slow render by delaying for 1 second | |
| // Used to demonstrate concurrent rendering with async_react_component | |
| const DelayedComponent = ({ index, delayMs = 1000 }) => ( | |
| <div style={{ padding: '10px', margin: '5px', border: '1px solid #ccc' }}> | |
| <strong>Component {index}</strong> - Rendered after {delayMs}ms delay | |
| </div> | |
| ); | |
| import React from 'react'; | |
| // Component that simulates a slow render by delaying for 1 second | |
| // Used to demonstrate concurrent rendering with async_react_component | |
| const DelayedComponent = ({ index, delayMs = 1000 }) => ( | |
| <div style={{ padding: '10px', margin: '5px', border: '1px solid #ccc' }}> | |
| <strong>Component {index}</strong> - Rendered after {delayMs}ms delay | |
| </div> | |
| ); |
🧰 Tools
🪛 GitHub Actions: React on Rails Pro - Lint
[error] 1-1: ESLint: 'no-promise-executor-return' violation. Return values from promise executor functions cannot be read. (no-promise-executor-return)
🤖 Prompt for AI Agents
In
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
lines 1-11, remove the top `'use client'` directive because this file is a
server component (.server.jsx) and the directive is contradictory; delete the
first line, verify no client-only hooks or browser APIs are used later in the
file, keep the React import and the server-side async component/export as-is,
and run tests/build to ensure no client-only code remains.
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
Show resolved
Hide resolved
- Add RBS signatures for AsyncValue, ImmediateAsyncValue, and AsyncRendering - Add CHANGELOG entry for the async_react_component feature 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Code Review: Async React Component RenderingThis is an excellent PR that brings significant performance improvements to React on Rails Pro. The implementation is well-designed, thoroughly tested, and follows best practices. Here's my detailed review: ✅ Strengths1. Clean Architecture & Design
2. Robust Error Handling
3. Excellent Test Coverage 🎯
4. Performance Optimization
5. Type Safety
6. Documentation & Examples
💡 Minor Suggestions (Non-blocking)1. Helper Method Location (react_on_rails_pro_helper.rb:237-275)The new async helpers are in Consideration: This is actually reasonable given Rails' architecture - helpers have access to controller instance variables. The error messages make the requirement clear. 2. Barrier Size Checking (async_rendering.rb:58)
Enhancement idea (future): Could potentially add config option to make this raise in test/development environments (stricter enforcement). 3. Test Helper Duplication (react_on_rails_pro_helper_spec.rb:336-363)The Minor: This is fine for tests, but consider extracting to a shared test helper file if this pattern grows. 4. Component Name in AsyncValue (async_value.rb:13-17)
Question: Is this for debugging/logging purposes? If so, consider documenting this intent. If not needed, could be removed to simplify. 🔍 Potential Issues (Please Verify)1. Async Context Leaking Across Requests
|
Use block statement in Promise executor to avoid implicit return of setTimeout's timer ID. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Code Review: Async React Component RenderingOverall Assessment: ✅ Excellent implementation! This is a well-architected feature with comprehensive testing and clear documentation. The code quality is high and follows the project's conventions. 🎯 StrengthsArchitecture & Design
Error Handling
Testing
Type Safety
🔍 Issues & Concerns🚨 CRITICAL: ESLint Warning in DelayedComponent.server.jsxLocation: await new Promise((resolve) => {
setTimeout(resolve, delayMs); // ⚠️ eslint: no-promise-executor-return
});Issue: According to commit 38d0dfd, this triggered an ESLint error: Expected fix (based on the commit message): await new Promise((resolve) => {
setTimeout(resolve, delayMs);
});Wait, actually this IS the fixed version. Let me check the actual linting... Actually, upon closer inspection of commit 38d0dfd, the issue was that the code originally had: await new Promise((resolve) => setTimeout(resolve, delayMs));And it was changed to use a block statement. The current code is correct! ✅ 🤔 Minor Concerns & Suggestions1. Performance: Cache Lookup on Every CallLocation: cached_result = Rails.cache.read(cache_key, cache_options)While synchronous cache lookup is good (avoids async overhead for hits), it means every call pays the cache lookup cost even when not needed. Question: Did you consider passing an option to bypass cache lookup for cases where developers know data is fresh? For example: cached_async_react_component("Card", cache_key: @product, skip_cache: @product.updated_recently?)Not a blocker, but worth considering for future optimization. 2. Resource Management: Barrier CleanupLocation: Sync do
@react_on_rails_async_barrier = Async::Barrier.new
yield
check_for_unresolved_async_components
ensure
@react_on_rails_async_barrier&.stop
@react_on_rails_async_barrier = nil
endGood: The Question: What happens if Looking at the code:
Verification needed: Does Suggestion: Add a comment documenting the expected behavior, or add a test case: it "terminates pending tasks when an exception occurs during render" do
# Test that barrier cleanup works even when yield raises
end3. Code Duplication in Helper MethodsLocation: The three methods Current: def render_async_react_component_uncached(component_name, raw_options, &block)
options = prepare_async_render_options(raw_options, &block)
task = @react_on_rails_async_barrier.async do
react_component(component_name, options)
end
ReactOnRailsPro::AsyncValue.new(component_name: component_name, task: task)
end
def render_async_react_component_with_cache(component_name, raw_options, cache_key, cache_options, &block)
options = prepare_async_render_options(raw_options, &block)
task = @react_on_rails_async_barrier.async do
result = react_component(component_name, options)
Rails.cache.write(cache_key, result, cache_options)
result
end
ReactOnRailsPro::AsyncValue.new(component_name: component_name, task: task)
endThese are nearly identical. Could be refactored to: def render_async_react_component_internal(component_name, raw_options, cache_key: nil, cache_options: nil, &block)
options = prepare_async_render_options(raw_options, &block)
task = @react_on_rails_async_barrier.async do
result = react_component(component_name, options)
Rails.cache.write(cache_key, result, cache_options) if cache_key
result
end
ReactOnRailsPro::AsyncValue.new(component_name: component_name, task: task)
endHowever: The current approach is more explicit and easier to debug. Since these are private methods, the duplication is acceptable. No change needed unless you prefer DRYer code. 4. Documentation: Missing Edge CaseLocation: # @example Enable for all except specific actions
# class ProductsController < ApplicationController
# include ReactOnRailsPro::AsyncRendering
# enable_async_react_rendering except: [:create, :update]
# endMissing example: What happens if you call Expected behavior:
Suggestion: Add to the module docs: # @note Calling async_react_component in actions not covered by enable_async_react_rendering
# will raise ReactOnRailsPro::Error with a helpful message5. Type Safety:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
react_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs (1)
1-14: AsyncRendering concern signatures look correct; optional type tightening laterThe module nesting,
ClassMethods.enable_async_react_rendering(**untyped options) -> void, the instance ivar@react_on_rails_async_barrier, and the private instance methods all line up with the described concern behavior and Rails around_action usage. If/when you add RBS forAsync::Barrierand controller/response types, you could optionally tighten these fromuntyped(e.g.,@react_on_rails_async_barrier: ::Async::Barrier?and a more specific return type forwrap_in_async_react_context), but the current surface is fine for an initial introduction. Based on learnings, this matches the intended mapping to the corresponding Ruby concern inlib/react_on_rails_pro/concerns/async_rendering.rb.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
react_on_rails_pro/CHANGELOG.md(1 hunks)react_on_rails_pro/sig/react_on_rails_pro/async_value.rbs(1 hunks)react_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs(1 hunks)react_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbs(1 hunks)react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/DelayedComponent.server.jsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS ensure files end with a newline character before committing/pushing
Files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
🧠 Learnings (18)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes (features, bug fixes, breaking changes, deprecations, performance improvements) to the open-source React on Rails gem and npm package
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG*.md : Use changelog format: `[PR 1818](https://github.com/shakacode/react_on_rails/pull/1818) by [username](https://github.com/username)` (no hash in PR number)
Applied to files:
react_on_rails_pro/CHANGELOG.md
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG_PRO.md : Update `/CHANGELOG_PRO.md` for Pro-only features, fixes, and changes affecting Pro packages
Applied to files:
react_on_rails_pro/CHANGELOG.md
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/CHANGELOG.md
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to lib/react_on_rails/*.rb : Create RBS signature files for new Ruby files in `lib/react_on_rails/` by adding corresponding `.rbs` file in `sig/react_on_rails/`, adding to Steepfile, and validating before committing
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/CHANGELOG.md
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: The `react_on_rails_pro/` directory has its own Prettier/ESLint configuration and must be linted separately
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to sig/react_on_rails/**/*.rbs : In RBS type signature files, define type signatures that correspond to Ruby files in `lib/react_on_rails/`
Applied to files:
react_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Applied to files:
react_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbsreact_on_rails_pro/sig/react_on_rails_pro/concerns/async_rendering.rbs
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.
Applied to files:
react_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: pro-lint-js-and-ruby
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: claude-review
🔇 Additional comments (5)
react_on_rails_pro/CHANGELOG.md (2)
28-28: Changelog entry format and content look good.The async React component rendering entry follows the established changelog format with the correct PR reference and contributor attribution. The description clearly conveys the feature benefit (parallel HTTP requests) and controller-level requirement.
28-28: File ends with newline character as required.The changelog entry at line 28 follows the correct format guideline (PR number by username with no hash), and the file properly ends with a newline character per coding standards.
react_on_rails_pro/sig/react_on_rails_pro/immediate_async_value.rbs (2)
1-15: File correctly ends with a newline character.Verification confirms the file already ends with a newline as required by the coding guidelines. No action needed.
1-15: Interface compatibility concern is invalid;component_nameis not accessed by calling code.Testing and helper code searches reveal that calling code accesses only the
.valueproperty on bothAsyncValueandImmediateAsyncValuereturn values. The.component_namereader is tested only inasync_value_spec.rbonAsyncValueinstances directly, not on the return values fromcached_async_react_componentorasync_react_componenthelpers. Both classes are compatible for duck typing through their actual public interface (.value,.resolved?,.to_s,.html_safe). The file correctly ends with a newline.react_on_rails_pro/sig/react_on_rails_pro/async_value.rbs (1)
1-18: File meets all requirements; type signatures are appropriate and newline is present.The RBS type signatures for
AsyncValueare well-defined and appropriate for an async rendering wrapper. The method signatures (value,resolved?,to_s,html_safe) align with the async rendering flow. The file correctly ends with a newline character, satisfying the coding guideline requirement.
The component_name attribute was unused and unnecessary. This change simplifies AsyncValue to only store the task, matching the interface of ImmediateAsyncValue for consistency. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Code Review - PR #2139: Async React Component RenderingGreat work on implementing concurrent React component rendering! This is a well-designed feature with solid test coverage. Here's my detailed feedback: ✅ Strengths
🔍 Potential Issues & Suggestions1. Resource Cleanup in AsyncRendering ConcernLocation: The def wrap_in_async_react_context
Sync do
@react_on_rails_async_barrier = Async::Barrier.new
yield
check_for_unresolved_async_components # Called here
ensure
@react_on_rails_async_barrier&.stop # Then stopped here
@react_on_rails_async_barrier = nil
end
endIssue: If Suggestion: Move the check into the def wrap_in_async_react_context
Sync do
@react_on_rails_async_barrier = Async::Barrier.new
yield
ensure
check_for_unresolved_async_components if @react_on_rails_async_barrier
@react_on_rails_async_barrier&.stop
@react_on_rails_async_barrier = nil
end
end2. Block Evaluation Timing in
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
react_on_rails_pro/sig/react_on_rails_pro/async_value.rbs (1)
1-15: Consider more specific types for better type safety.The RBS signatures use
untypedextensively, which is pragmatic for an initial implementation but reduces type safety benefits. Consider these refinements when async gem types become available:
- Line 3:
@task: Async::Task[untyped](if async gem provides RBS types)- Line 13:
html_safe: () -> ActiveSupport::SafeBuffer(Rails' typical return type)- Line 7:
value: () -> String(if render results are always strings)react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb (1)
34-48: Consider more deterministic synchronization for test reliability.The test uses
sleep 0.1on line 39 to ensure the task hasn't completed when checkingresolved?on line 44. While 0.1 seconds is typically sufficient, this creates a theoretical race condition in very fast test environments.For more deterministic testing, consider using a synchronization primitive (e.g., Queue, condition variable) to ensure the task is provably waiting when
resolved?is checked. However, the current approach is pragmatic and unlikely to cause issues in practice.react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb (1)
220-249: Async helper wiring looks correct; consider adding tracing + shared barrier guardThe barrier guard and Async::Barrier usage look sound, and returning
ReactOnRailsPro::AsyncValue.new(task: task)matches the intended.valueaccess pattern. For better observability and consistency with other helpers in this module, consider wrapping the body inReactOnRailsPro::Utils.with_trace(component_name)and/or extracting the repeated barrier check into a small private helper (e.g.,ensure_async_barrier!) reused here and infetch_async_react_component. Also, since this introduces new public helpers, double‑check that/CHANGELOG.mdis updated and thatbundle exec rubocoppasses for this file. Based on learnings, ...
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb(2 hunks)react_on_rails_pro/lib/react_on_rails_pro/async_value.rb(1 hunks)react_on_rails_pro/sig/react_on_rails_pro/async_value.rbs(1 hunks)react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- react_on_rails_pro/lib/react_on_rails_pro/async_value.rb
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{rb,rake}
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS run
bundle exec rubocopand fix ALL violations before every commit/push
Files:
react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS ensure files end with a newline character before committing/pushing
Files:
react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbs
🧠 Learnings (13)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to spec/dummy/e2e/playwright/**/*.spec.js : Use Rails helpers (`appFactories`, `appEval`) in Playwright tests instead of UI-based setup to leverage factory_bot and arbitrary Ruby code execution
Applied to files:
react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to sig/react_on_rails/**/*.rbs : In RBS type signature files, define type signatures that correspond to Ruby files in `lib/react_on_rails/`
Applied to files:
react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rbreact_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbs
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbs
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to /CHANGELOG.md : Update `/CHANGELOG.md` for user-visible changes (features, bug fixes, breaking changes, deprecations, performance improvements) to the open-source React on Rails gem and npm package
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-11-25T08:05:17.804Z
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T08:05:17.804Z
Learning: Applies to lib/react_on_rails/*.rb : Create RBS signature files for new Ruby files in `lib/react_on_rails/` by adding corresponding `.rbs` file in `sig/react_on_rails/`, adding to Steepfile, and validating before committing
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbs
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rbreact_on_rails_pro/sig/react_on_rails_pro/async_value.rbs
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Applied to files:
react_on_rails_pro/sig/react_on_rails_pro/async_value.rbs
🧬 Code graph analysis (1)
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb (3)
react_on_rails_pro/lib/react_on_rails_pro/utils.rb (1)
with_trace(155-167)react_on_rails_pro/lib/react_on_rails_pro/cache.rb (2)
use_cache?(31-39)react_component_cache_key(70-85)react_on_rails_pro/lib/react_on_rails_pro/server_rendering_pool/pro_rendering.rb (1)
cache_key(101-109)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: pro-lint-js-and-ruby
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: claude-review
🔇 Additional comments (6)
react_on_rails_pro/spec/react_on_rails_pro/async_value_spec.rb (5)
1-6: LGTM!Proper frozen_string_literal directive and appropriate requires for async testing.
8-31: LGTM!Comprehensive tests for the
#valuemethod covering both success and error propagation paths. Proper use ofSyncblocks for async gem testing.
63-74: LGTM!Straightforward test validating string representation of the async value.
76-90: LGTM!Proper validation of
html_safedelegation, confirming both the HTML-safe status and content preservation.
1-92: Ensure rubocop compliance and file ends with newline.Per coding guidelines:
- Verify the file ends with a newline character.
- Run
bundle exec rubocopon this file and fix all violations before committing.As per coding guidelines for Ruby files.
react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb (1)
270-275: cached_async_react_component API aligns with existing caching helpersThis wrapper correctly reuses
check_caching_options!so props are only provided via the block andcache_keyis enforced, then delegates to the async fetch implementation underwith_trace. The overall shape matchescached_react_componentand should be easy for callers to adopt.
| # Async version of fetch_react_component. Handles cache lookup synchronously, | ||
| # returns ImmediateAsyncValue on hit, AsyncValue on miss. | ||
| def fetch_async_react_component(component_name, raw_options, &block) | ||
| unless defined?(@react_on_rails_async_barrier) && @react_on_rails_async_barrier | ||
| raise ReactOnRailsPro::Error, | ||
| "cached_async_react_component requires AsyncRendering concern. " \ | ||
| "Include ReactOnRailsPro::AsyncRendering in your controller and call enable_async_react_rendering." | ||
| end | ||
|
|
||
| # Check conditional caching (:if / :unless options) | ||
| unless ReactOnRailsPro::Cache.use_cache?(raw_options) | ||
| return render_async_react_component_uncached(component_name, raw_options, &block) | ||
| end | ||
|
|
||
| cache_key = ReactOnRailsPro::Cache.react_component_cache_key(component_name, raw_options) | ||
| cache_options = raw_options[:cache_options] || {} | ||
| Rails.logger.debug { "React on Rails Pro async cache_key is #{cache_key.inspect}" } | ||
|
|
||
| # Synchronous cache lookup | ||
| cached_result = Rails.cache.read(cache_key, cache_options) | ||
| if cached_result | ||
| Rails.logger.debug { "React on Rails Pro async cache HIT for #{cache_key.inspect}" } | ||
| render_options = ReactOnRails::ReactComponent::RenderOptions.new( | ||
| react_component_name: component_name, | ||
| options: raw_options | ||
| ) | ||
| load_pack_for_generated_component(component_name, render_options) | ||
| return ReactOnRailsPro::ImmediateAsyncValue.new(cached_result) | ||
| end | ||
|
|
||
| Rails.logger.debug { "React on Rails Pro async cache MISS for #{cache_key.inspect}" } | ||
| render_async_react_component_with_cache(component_name, raw_options, cache_key, cache_options, &block) | ||
| end | ||
|
|
||
| # Renders async without caching (when :if/:unless conditions disable cache) | ||
| def render_async_react_component_uncached(component_name, raw_options, &block) | ||
| options = prepare_async_render_options(raw_options, &block) | ||
|
|
||
| task = @react_on_rails_async_barrier.async do | ||
| react_component(component_name, options) | ||
| end | ||
|
|
||
| ReactOnRailsPro::AsyncValue.new(task: task) | ||
| end | ||
|
|
||
| # Renders async and writes to cache on completion | ||
| def render_async_react_component_with_cache(component_name, raw_options, cache_key, cache_options, &block) | ||
| options = prepare_async_render_options(raw_options, &block) | ||
|
|
||
| task = @react_on_rails_async_barrier.async do | ||
| result = react_component(component_name, options) | ||
| Rails.cache.write(cache_key, result, cache_options) | ||
| result | ||
| end | ||
|
|
||
| ReactOnRailsPro::AsyncValue.new(task: task) | ||
| end | ||
|
|
||
| def prepare_async_render_options(raw_options) | ||
| raw_options.merge( | ||
| props: yield, | ||
| skip_prerender_cache: true, | ||
| auto_load_bundle: ReactOnRails.configuration.auto_load_bundle || raw_options[:auto_load_bundle] | ||
| ) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll help you verify the concerns raised in this review comment. Let me start by examining the codebase to confirm the actual implementation and identify any issues.
<function_calls>
cd /tmp && git clone --depth 1 https://github.com/shakacode/react_on_rails.git 2>&1 | head -20
</function_calls>
Let me locate the file and examine the implementation:
<function_calls>
find /tmp/react_on_rails -name "react_on_rails_pro_helper.rb" -type f
</function_calls>
<function_calls>
find /tmp/react_on_rails -path "/react_on_rails_pro" -name "helper" -type f
</function_calls>
<function_calls>
ls -la /tmp/react_on_rails/ | head -30
</function_calls>
Let me search more broadly for helper files:
<function_calls>
find /tmp/react_on_rails -name "*helper.rb" -type f | head -20
</function_calls>
Now let me search for the async helper methods mentioned in the review:
<function_calls>
rg -n "fetch_async_react_component" /tmp/react_on_rails --type rb
</function_calls>
Let me search for the sync version to compare behavior:
<function_calls>
rg -n "def fetch_react_component" /tmp/react_on_rails -A 30 --type rb
</function_calls>
Now let me search for usage of the cache metadata keys mentioned:
<function_calls>
rg -n "RORP_CACHE_KEY|RORP_CACHE_HIT" /tmp/react_on_rails --type rb -B 2 -A 2
</function_calls>
Let me check the cache implementation to understand race_condition_ttl handling:
<function_calls>
rg -n "race_condition_ttl|Rails.cache.fetch" /tmp/react_on_rails --type rb -B 2 -A 2
</function_calls>
Let me look at the load_pack_for_generated_component method to understand what it does:
<function_calls>
rg -n "def load_pack_for_generated_component" /tmp/react_on_rails -A 15 --type rb
</function_calls>
Let me check the ReactComponent RenderOptions to see what properties it has:
<function_calls>
rg -n "class RenderOptions" /tmp/react_on_rails -A 30 --type rb
</function_calls>
Let me search for how ImmediateAsyncValue and AsyncValue are implemented:
<function_calls>
rg -n "class.*AsyncValue|ImmediateAsyncValue" /tmp/react_on_rails --type rb -A 10
</function_calls>
Let me check the prepare_async_render_options method more carefully:
<function_calls>
rg -n "def prepare_async_render_options" /tmp/react_on_rails -A 10 --type rb
</function_calls>
Let me look at usage of react_component_hash to see if metadata is relied upon:
<function_calls>
rg -n "react_component_hash" /tmp/react_on_rails --type rb -B 3 -A 3
</function_calls>
Now let me check how React.cache.read/write are used in the async method vs fetch:
<function_calls>
rg -n "Rails.cache.read|Rails.cache.write|Rails.cache.fetch" /tmp/react_on_rails --type rb | head -50
</function_calls>
Let me look at the actual file content around the specified lines to see the exact implementation:
<function_calls>
find /tmp/react_on_rails -name "*.rb" -type f -exec grep -l "fetch_async_react_component|render_async_react_component_uncached" {} ;
</function_calls>
Let me try a different approach and search the pro subdirectory:
<function_calls>
find /tmp/react_on_rails -type d -name "pro"
</function_calls>
Let me search for the method across all Ruby files:
<function_calls>
grep -r "fetch_async_react_component" /tmp/react_on_rails --include="*.rb" 2>/dev/null | head -5
</function_calls>
Let me check if this is a separate gem or part of react_on_rails:
<function_calls>
ls -la /tmp/react_on_rails/
</function_calls>
Let me search for any file that contains the async rendering code:
<function_calls>
grep -r "render_async_react_component_with_cache" /tmp/react_on_rails --include="*.rb" 2>/dev/null
</function_calls>
Let me check the git history to find the current branch:
<function_calls>
cd /tmp/react_on_rails && git log --oneline -10 && git branch -a
</function_calls>
Let me search all Ruby files for any part of the code in the review:
<function_calls>
grep -r "ImmediateAsyncValue|render_async_react_component_uncached" /tmp/react_on_rails --include="*.rb" 2>/dev/null
</function_calls>
Let me check if this is part of a new PR that hasn't been merged yet. Let me search for any recent changes:
<function_calls>
cd /tmp/react_on_rails && git log --all --oneline | grep -i async | head -10
</function_calls>
Let me look for the file using the exact path provided in the review:
<function_calls>
cat /tmp/react_on_rails/react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb 2>&1 | head -50
</function_calls>
Let me check what branches exist and if there's a specific branch for this PR:
<function_calls>
cd /tmp/react_on_rails && git branch -r | grep -i async
</function_calls>
Let me find all available branches:
<function_calls>
cd /tmp/react_on_rails && git branch -r | head -20
</function_calls>
Based on my initial investigation, let me take a different approach and look for existing code to understand the pattern. Let me check the current main branch for the non-async version:
<function_calls>
grep -r "def fetch_react_component|def react_component" /tmp/react_on_rails --include="*.rb" -A 5 | head -100
</function_calls>
Let me look for react_component_cache_key and Cache class:
<function_calls>
grep -r "class Cache|react_component_cache_key" /tmp/react_on_rails --include="*.rb" -B 2 -A 10 | head -150
</function_calls>
Let me check for any mentions of RORP_ prefixed constants:
<function_calls>
grep -r "RORP_|:RORP_" /tmp/react_on_rails --include="*.rb" -B 2 -A 2
</function_calls>
Let me verify if this code review is for a branch or PR that needs to be checked out. Let me look at all tags and see if there's more context:
<function_calls>
cd /tmp/react_on_rails && git tag | tail -20
</function_calls>
Let me check the repo structure more carefully:
<function_calls>
find /tmp/react_on_rails -type f -name "*.rb" | grep -i helper | head -20
</function_calls>
Given the learnings provided, let me search for helper files and caching patterns that exist in the current main branch:
<function_calls>
find /tmp/react_on_rails -name "*helper.rb" -type f | xargs wc -l | sort -rn | head -20
</function_calls>
Let me examine one of the largest helper files:
<function_calls>
find /tmp/react_on_rails -name "*helper.rb" -type f -exec wc -l {} + | sort -rn | head -1 | awk '{print $2}' | xargs cat | head -200
</function_calls>
Let me try a more direct approach - search for any class or module that has async in its name:
<function_calls>
grep -r "class.*Async|module.Async" /tmp/react_on_rails --include=".rb" -B 1 -A 20
</function_calls>
Let me check the learnings more carefully - they reference specific files. Let me look at one of those:
<function_calls>
cat /tmp/react_on_rails/lib/react_on_rails/utils.rb 2>&1 | sed -n '112,124p'
</function_calls>
Now let me verify the structure and look for React on Rails Pro-specific code:
<function_calls>
find /tmp/react_on_rails -type d | grep -i pro
</function_calls>
Based on the learnings, react_on_rails_pro seems to be integrated. Let me look for the pro code in lib:
<function_calls>
find /tmp/react_on_rails/lib -type f -name "*.rb" | xargs grep -l "async" | head -10
</function_calls>
Let me look at the file structure to understand how React on Rails Pro is organized:
<function_calls>
ls -la /tmp/react_on_rails/lib/react_on_rails/
</function_calls>
Let me verify the current status by looking at recent commits:
<function_calls>
cd /tmp/react_on_rails && git log --oneline -20
</function_calls>
Let me check if there's a separate react_on_rails_pro directory at the root:
<function_calls>
ls -la /tmp/react_on_rails/ | grep -E "^d"
</function_calls>
Based on my exploration, the code in the review appears to be new/in-development code that's not yet in the cloned repository (which is at the main branch's latest commit). Let me analyze the review comment based on the patterns and context from the learnings and existing code structure I can observe.
Let me search for existing cache-related implementations to understand the current patterns:
<function_calls>
grep -r "Rails.cache|cache_key|cache_options" /tmp/react_on_rails/lib --include="*.rb" | grep -E "(def|cache)" | head -30
</function_calls>
Let me check if prepare_async_render_options is mentioned anywhere:
<function_calls>
grep -r "prepare.*render.options|skip_prerender_cache|auto_load_bundle" /tmp/react_on_rails --include=".rb" -B 2 -A 5
</function_calls>
Let me verify the block parameter handling - the code uses &block but the method definition shows it only takes raw_options:
<function_calls>
grep -r "prepare_async_render_options" /tmp/react_on_rails --include="*.rb" -B 5 -A 10
</function_calls>
Based on my thorough analysis, I can now see a critical issue in the provided code snippet. Let me verify the method signature issue more carefully by examining the review comment again. The prepare_async_render_options method definition in the snippet doesn't include the &block parameter, but it's being called with &block in the two calling methods. This is a clear bug.
Let me verify this is indeed an issue with the code as presented:
<function_calls>
cat << 'EOF' | python3
code_snippet = """
def render_async_react_component_uncached(component_name, raw_options, &block)
options = prepare_async_render_options(raw_options, &block)
# ...
end
def render_async_react_component_with_cache(component_name, raw_options, cache_key, cache_options, &block)
options = prepare_async_render_options(raw_options, &block)
# ...
end
def prepare_async_render_options(raw_options)
raw_options.merge(
props: yield,
# ...
)
end
"""
Check the issue
print("Issue Found:")
print("- prepare_async_render_options is called with &block in lines 2 and 11")
print("- But prepare_async_render_options definition at line 14 does NOT have &block parameter")
print("- However, the method uses 'yield' at line 16, which requires a block")
print("- This is a critical bug - the method signature must include &block or a block is required")
EOF
</function_calls>
Based on my analysis of the code snippet in the review comment, I've identified a critical bug that needs to be flagged. Here's my rewritten review:
Critical: prepare_async_render_options method signature missing required &block parameter
The method is called with &block in both render_async_react_component_uncached (line 390) and render_async_react_component_with_cache (line 408), but the method definition at line 414 omits the &block parameter. Since the method uses yield at line 417 to evaluate props, this will raise a LocalJumpError ("no block given") at runtime. Add &block to the method signature:
def prepare_async_render_options(raw_options, &block)Async cache path: confirm semantics vs existing fetch_react_component (race_condition_ttl, metadata, and cache writes)
Beyond the signature bug, a few architectural points to verify/consider:
- Compared to
fetch_react_component, switching fromRails.cache.fetchto separateread/writechanges how:race_condition_ttlbehaves (it's only honored byfetchin Rails). If callers rely onrace_condition_ttlfor hot keys, document thatcached_async_react_componentdoes not provide identical race-condition behavior. - On cache hits you call
load_pack_for_generated_component, which matches the non-async helper, but you don't attach:RORP_CACHE_KEY/:RORP_CACHE_HITmetadata when the result is a Hash likefetch_react_componentdoes. If downstream code/tests depend on those fields, align this behavior or document the difference. prepare_async_render_optionscorrectly ensures props are only evaluated once per cache miss and setsskip_prerender_cache: trueandauto_load_bundleconsistently.
🤖 Prompt for AI Agents
In react_on_rails_pro/app/helpers/react_on_rails_pro_helper.rb around lines
358-422, prepare_async_render_options is defined without a block parameter but
callers pass &block and the method uses yield, which will raise a
LocalJumpError; fix by adding &block to the method signature (def
prepare_async_render_options(raw_options, &block)) and ensure the method either
uses yield or explicitly calls block.call to obtain props so the forwarded block
is consumed correctly.
Summary
Adds concurrent React component rendering support for React on Rails Pro. Closes #2138.
async_react_component: ReturnsAsyncValueimmediately, renders components concurrentlycached_async_react_component: Async rendering with fragment caching support (same options ascached_react_component)AsyncRenderingconcern: Controller mixin withenable_async_react_renderingclass methodMotivation
Currently, multiple
react_componentcalls in a Rails view run sequentially. Each call makes an HTTP request to the Node renderer, causing delays proportional to the number of components. This PR enables concurrent execution using theasyncgem (already used in the codebase for streaming).Usage
Controller Setup
View Usage
With Caching
Implementation Details
Async::Barrierfor coordinating concurrent tasks within a requestImmediateAsyncValue(no async overhead)task.waitcached_react_componentoptions::cache_key,:cache_options,:if,:unlessTest plan
AsyncValueclassImmediateAsyncValueclassasync_react_componentcached_async_react_component:if/:unless) tests🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.